//
// reload.cpp
// reload
// Reloads data from unload.<applnbr> file into application database
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

#include <xercesc/sax2/XMLReaderFactory.hpp>
#include <xercesc/sax2/DefaultHandler.hpp>
#include <xercesc/sax2/Attributes.hpp>
#include <xercesc/framework/XMLFormatter.hpp>

#include "../SDK/ToolsInterface.h"
#include "../OSDep/utf8.h"

//
// Structure declarations
//
struct fieldinfo {
	char name[ 33 ];
	int number;

	int type;
	int length;
	int dec_places;
	int flags;

	bool first;
	char* data;
	int len;
};



//
// Global variables
//
XCTxn* txn = NULL;
int app_number;
char inexname[ 255 ];
long buffernumber;
char buffername[ 32 ];
fieldinfo values[255];

XCBuffer* buffer = NULL;

bool GotData = false;
char* SQL = NULL;

int valcount;
int fieldcount;

char app_string[ 10 ];

char* new_appdic = NULL;
long new_appdic_size = 0;
buffer_header* header;
buffer_db_defn* database;


//
// Class declarations
//
class XCReload : public XCAppInterface, public XERCES_CPP_NAMESPACE::DefaultHandler {
public:
	XCReload() {
		errors = false;
		Application = false;
		InBuffer = false;
		InStructure = false;
		InRecord = false;
		InField = false;
		GotData = false;

		query = new XCSQLGen( this );
	};

	int Initialize() {
		int result = XCAppInterface::Initialize( "Reload", app_number );
		return result;
	};

	// Xerces error handlers...
	bool errors;
	void warning( const XERCES_CPP_NAMESPACE::SAXParseException& exception ) { cout << "WARNING: " << UniTranslator(exception.getMessage()).UTF8() << endl; };
	void error( const XERCES_CPP_NAMESPACE::SAXParseException& exception ) { cout << "ERROR: " << UniTranslator( exception.getMessage() ).UTF8() << endl; };
	void fatalError( const XERCES_CPP_NAMESPACE::SAXParseException& exception ) { cout << "FATAL: " << UniTranslator( exception.getMessage() ).UTF8() << endl; };

	// SAX Parser
	void startElement( const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname, const XERCES_CPP_NAMESPACE::Attributes& atts ) {
		UniTranslator name( localname );

//		printf( "<%s>", name.UTF8() );
		if (!Application) {
			if (strcmp( name, "Application" )==0) {
//				printf( "Application: %s\n", name );
				HandleApplication( true, &atts );
			};
		} else {
			if (strcmp( name, "Buffer" )==0) {
//				printf( "Buffer: %s\n", name );
				HandleBuffer( true, &atts );
			} else if (strcmp( name, "STRUCTURE" )==0) {
//				printf( "Structure: %s\n", name );
				HandleStructure( true, &atts );
			} else if (strcmp( name, "RECORD" )==0)
				HandleRecord( true, &atts );
			else if (strcmp( name, "Field" )==0)
				HandleField( true, &atts );
			else {
				cout << "Bad XML Start Tag: " << name.UTF8() << endl;
			};
		};
	};

	void characters( const XMLCh* const chars, const unsigned int length ) {
		if (InRecord && InField) {
			fieldinfo* field = &values[ valcount ];
			UniTranslator data( chars );
//			printf( "\nField %s value = \"%s\"\n", field->name, data.UTF8() );
			if (field->first) {
				field->first = false;
				strcpy( field->data, data );
			} else
				strcat( field->data, data );
			field->len = strlen( field->data );
			GotData = true;
		};
	};

	void endElement( const XMLCh* const uri, const XMLCh* const localname, const XMLCh* const qname ) {
		UniTranslator name( localname );

//		printf( "</%s>", name.UTF8() );
		if (strcmp( name, "Application" )==0) {
//			printf( "Application (exit): %s\n", name );
			HandleApplication( false, NULL );
		} else if (strcmp( name, "Buffer" )==0) {
//			printf( "Buffer (exit): %s\n", name );
			HandleBuffer( false, NULL );
		} else if (strcmp( name, "STRUCTURE" )==0) {
//			printf( "Structure (exit): %s\n", name );
			HandleStructure( false, NULL );
		} else if (strcmp( name, "RECORD" )==0)
			HandleRecord( false, NULL );
		else if (strcmp( name, "Field" )==0)
			HandleField( false, NULL );
	};

	void HandleApplication( bool start, const XERCES_CPP_NAMESPACE::Attributes* atts );
	void HandleBuffer( bool start, const XERCES_CPP_NAMESPACE::Attributes* atts );
	void HandleStructure( bool start, const XERCES_CPP_NAMESPACE::Attributes* atts );
	void HandleRecord( bool start, const XERCES_CPP_NAMESPACE::Attributes* atts );
	void HandleField( bool start, const XERCES_CPP_NAMESPACE::Attributes* atts );

	bool Application;
	bool InBuffer;
	bool InStructure;
	bool InRecord;
	bool InField;
	bool GotData;

	int record_num;

	XCSQLGen* query;
};


////////////////////////////////////
//
// XML Parsing functions
//
////////////////////////////////////


void XCReload::HandleField( bool start, const XERCES_CPP_NAMESPACE::Attributes* attrib ) {
	InField = start;
	if (start) {
		if (InStructure) {
			fieldinfo* FieldInfo = &values[ fieldcount ];
			int len = attrib->getLength();

			for (int i=0; i<len; i++) {
				UniTranslator name( attrib->getLocalName( i ) );
				UniTranslator value( attrib->getValue( i ) );

				if (strcmp( name, "Name" )==0) {
					strcpy( FieldInfo->name, value );
				} else if (strcmp( name, "Number" )==0)
					FieldInfo->number = atoi( value );
				else if (strcmp( name, "Type" )==0)
					FieldInfo->type = atoi( value );
				else if (strcmp( name, "Length" )==0)
					FieldInfo->length = atoi( value );
				else if (strcmp( name, "Dec_Places" )==0)
					FieldInfo->dec_places = atoi( value );
				else if (strcmp( name, "Flags" )==0)
					FieldInfo->flags = atoi( value );
			};

			FieldInfo->data = new char[ (FieldInfo->length*4)+2 ];
			*FieldInfo->data = 0;
			fieldcount++;
		} else if (InRecord) {	// Field record
			// Clear the field value
			fieldinfo* field = &values[ valcount ];
			*field->data = 0;

			XCField* fld = buffer->GetField( field->number );
			if (fld)
				fld->SetDefault();
			field->first = true;
		};
	} else {
		if (InRecord) {
			if (GotData) {
				fieldinfo* temp = &values[ valcount ];
				XCField* fld = buffer->GetField( temp->number );
				try {
					if (fld) {
						int type = fld->GetType();
						if (type == XCBLOB)
							fld->SetBLOBbase64( strlen(temp->data), temp->data );
						else
							fld->SetString( temp->data );
					};
				} catch( ... ) {
					printf( "Error loading record %d, Field:%s, value '%s'\n", record_num, fld->GetFieldName(), temp->data );
					throw;
				};
			};
			valcount++;
		};
	};
};




void XCReload::HandleRecord( bool start, const XERCES_CPP_NAMESPACE::Attributes* atts ) {
	InRecord = start;
	if (start) {
		// Increase the record count
		record_num++;

		// Clear data from fields
		buffer->LoadDefaults();

		// Start to prepare the insert statement
		query->MakeINSERT( buffer );

		// Reset our counter
		valcount = 0;
	} else {
		// Write the changes to the database
		XCTxn* txn = GetDatabase()->CreateTxn();
		try {
			txn->ExecuteSQL( query, SQL_NO_RESULT );
			txn->EndTxn( true );
		} catch( ... ) {
			printf( "Error loading record %d, database failure\n", record_num );
			XCAppOptions appopt;
			GetOptions( appopt );
			appopt.debug_type_filter = TRC_BUFFER | TRC_NOLABEL;
			appopt.debug_level_cutoff[ TCL_NOLABEL ] = TLV_HIGH;
			SetOptions( appopt );

			buffer->DebugDump();

			appopt.debug_type_filter = 0;
			SetOptions( appopt );

			txn->EndTxn( false );
			throw;
		};
	};
};




void XCReload::HandleStructure( bool start, const XERCES_CPP_NAMESPACE::Attributes* attrib ) {
	InStructure = start;
	if (start) {
		fieldcount = 0;
	} else {
		query->MakeDELETE( buffer );
		txn->ExecuteSQL( query, SQL_NO_RESULT );
	};
};





void XCReload::HandleBuffer( bool start, const XERCES_CPP_NAMESPACE::Attributes* attrib ) {
	InBuffer = start;
	if (start) {
		// Reset the record count
		record_num = 0;

		int len = attrib->getLength();
		for (int i=0; i<len; i++) {
			UniTranslator name( attrib->getLocalName( i ) );
			UniTranslator value( attrib->getValue( i ) );

			if (strcmp( name, "Number" )==0) {
				buffernumber = atoi( value );
			} else if (strcmp( name, "Name" ) ==0) {
				strcpy( buffername, value );
			} else
				printf( "INVALID XML!!\n" );
		};

		buffer = CreateBuffer( buffernumber );
		cout << "Working on buffer: " << buffername << endl;
		fieldcount = 0;
	} else {
		buffer->Release();

		for (int i=0; i<fieldcount; i++)
			delete [] values[ i ].data;
	};
};




void XCReload::HandleApplication( bool start, const XERCES_CPP_NAMESPACE::Attributes* atts ) {
	if (start)
		Application = true;
	else
		Application = false;
};



///////////////////////////////////////////////////
//
// Program entry point
//
///////////////////////////////////////////////////

int main(int argc, char* argv[]) {
	char* text = NULL;
	XCAppOptions myopts;
	int result = 0;

	Protea::ShowIntro( "reload" );

	if (argc > 1 && !strcmp(argv[1],"-L"))
	{
		Protea::ShowLicense();
		return 0;
	};

	Protea::Initialize();

	XCReload LoadApp;

	try {
		if (argc != 3) {
			cout << "Usage: reload [ApplicationNumber] [filename]" << endl;
			return 1;
		};

		SQL = new char[ 65535 ];
		app_number = atoi( argv[1] );
		if (app_number < 2) {
			cout << "Invalid application" << endl;
			return 1;
		};

		cout << "Loading APPDIC information for application: " << app_number << endl;

		LoadApp.GetOptions( myopts );
		myopts.debug_level_cutoff[ TCL_APPINTERFACE ] = TLV_MEDIUM;
		for ( int i = 0; i < num_tc; i++ )
			myopts.debug_level_cutoff[ i ] = 0xFF;
//		myopts.debug_type_filter = 0xFFFF;
//		myopts.debug_type_filter = TRC_DATABASE;
//		myopts.debug_type_filter = TRC_BUFFER;
		myopts.debug_type_filter = 0;
		myopts.appdic_first_run = true;
		LoadApp.SetOptions( myopts );
		LoadApp.Initialize();

		// Start database transaction
		LoadApp.ToolsTrace( TRC_APPINTERFACE, TLV_LOW, "Start transaction" );
		txn = LoadApp.GetDatabase()->CreateTxn();

		// Start SAX Parse
		LoadApp.ToolsTrace( TRC_APPINTERFACE, TLV_LOW, "Parse XML" );
		XERCES_CPP_NAMESPACE::SAX2XMLReader* parser = XERCES_CPP_NAMESPACE::XMLReaderFactory::createXMLReader();

		parser->setContentHandler( &LoadApp );
		parser->setErrorHandler( &LoadApp );
		parser->parse( argv[2] );
		delete parser;

		if (LoadApp.errors) {
			txn->EndTxn( false );
			return 1;
		};
		txn->EndTxn( true );
	} catch( XCToolsError& error ) {
		LoadApp.ToolsTrace( TRC_APPINTERFACE, TLV_LOW, "Caught tools error" );
		XCOutputCallStack( &LoadApp, error.GetErrorNumber(),error.GetDescription() );

		int field = error.GetField();
		printf( "Field: %d\n", field );

		if (txn)
			txn->EndTxn( false );
		result = error.GetErrorNumber();
	} catch( ... ) {
		result = 1;
		LoadApp.ToolsTrace( TRC_APPINTERFACE, TLV_LOW, "Caught unknown error" );
		XCOutputCallStack( &LoadApp, -1, NULL );
		if (txn)
			txn->EndTxn( false );
	};

	// Cleanup
	if (SQL)
		delete [] SQL;

	if (text)
		delete [] text;

	if (new_appdic)
		delete [] new_appdic;

	cout << endl << "Reload process completed..." << endl << endl;

	Protea::Uninitialize();

	return result;
};
